%%%-------------------------------------------------------------------
%%% @author shisj
%%% @copyright (C) 2016, <COMPANY>
%%% @doc
%%% 主工具集和
%%% @end
%%% Created : 29. 六月 2016 11:07
%%%-------------------------------------------------------------------
-module(tools).

-include_lib("wx/include/wx.hrl").

-behaviour(wx_object).
-export([start/0, start/1, start_link/0, start_link/1, format/3,
  init/1, terminate/2,  code_change/3,
  handle_info/2, handle_call/3, handle_cast/2, handle_event/2,
  messageDialog/2]).


-record(state, {win, demo, example, selector, log, doc}).

%% For wx-2.9 usage
-ifndef(wxSTC_ERLANG_COMMENT_FUNCTION).
-define(wxSTC_ERLANG_COMMENT_FUNCTION, 14).
-define(wxSTC_ERLANG_COMMENT_MODULE, 15).
-define(wxSTC_ERLANG_COMMENT_DOC, 16).
-define(wxSTC_ERLANG_COMMENT_DOC_MACRO, 17).
-define(wxSTC_ERLANG_ATOM_QUOTED, 18).
-define(wxSTC_ERLANG_MACRO_QUOTED, 19).
-define(wxSTC_ERLANG_RECORD_QUOTED, 20).
-define(wxSTC_ERLANG_NODE_NAME_QUOTED, 21).
-define(wxSTC_ERLANG_BIFS, 22).
-define(wxSTC_ERLANG_MODULES, 23).
-define(wxSTC_ERLANG_MODULES_ATT, 24).
-endif.

start() ->
  start([]).

start(Debug) ->
  wx_object:start(?MODULE, Debug, []).

start_link() ->
  start_link([]).

start_link(Debug) ->
  wx_object:start_link(?MODULE, Debug, []).

format(Config,Str,Args) ->
  Log = proplists:get_value(log, Config),
  wxTextCtrl:appendText(Log, io_lib:format(Str, Args)),
  ok.

messageDialog(Config,Str) ->
  Frame = proplists:get_value(win, Config),
  Dialog = wxMessageDialog:new(Frame,Str),
  wxMessageDialog:showModal(Dialog),
  wxMessageDialog:destroy(Dialog).

-define(DEBUG_NONE, 101).
-define(DEBUG_VERBOSE, 102).
-define(DEBUG_TRACE, 103).
-define(DEBUG_DRIVER, 104).

init(Options) ->
  wx:new(Options),
  process_flag(trap_exit, true),

  Frame = wxFrame:new(wx:null(), ?wxID_ANY, "乐基工具包", [{size,{1024,768}}]),

  %%添加菜单栏
  MB = wxMenuBar:new(),
  Help    = wxMenu:new([]),
  wxMenu:append(Help, ?wxID_HELP, "Help"),
  wxMenuBar:append(MB, Help, "&Help"),
  wxFrame:setMenuBar(Frame,MB),

  %%添加菜单选择和关闭窗口连接
  wxFrame:connect(Frame, command_menu_selected),
  wxFrame:connect(Frame, close_window),

  _SB = wxFrame:createStatusBar(Frame,[]),

  %%   T        Uppersplitter
  %%   O        Left   |    Right
  %%   P  Widgets|Doc |    Demo
  %%   S  -------------------------------
  %%   P          Log Window
  TopSplitter   = wxSplitterWindow:new(Frame, [{style, ?wxSP_NOBORDER}]),
  UpperSplitter = wxSplitterWindow:new(TopSplitter, [{style, ?wxSP_NOBORDER}]),
  LeftSplitter  = wxSplitterWindow:new(UpperSplitter, [{style, ?wxSP_NOBORDER}]),
  %% Setup so that sizers and initial sizes, resizes the windows correct
  wxSplitterWindow:setSashGravity(TopSplitter,   0.60),
  wxSplitterWindow:setSashGravity(UpperSplitter, 0.60),
  wxSplitterWindow:setSashGravity(LeftSplitter,  0.20),

  %% LeftSplitter:
  Example = fun(Beam) ->
      "lj_" ++ F = filename:rootname(Beam),
    F
            end,
  Mods = [Example(F) || F <- filelib:wildcard("lj_*.beam")],

  CreateLB = fun(Parent) ->
    wxListBox:new(Parent, ?wxID_ANY,
      [{style, ?wxLB_SINGLE},
        {choices, Mods}])
             end,
  {LBPanel, [LB],_} = create_subwindow(LeftSplitter, "工具", [CreateLB]),
  wxListBox:setSelection(LB, 0),
  wxListBox:connect(LB, command_listbox_selected),

  CreateCode = fun(Parent) ->
    code_area(Parent)
               end,
  {CodePanel, [Doc],_} = create_subwindow(LeftSplitter, "工具使用说明", [CreateCode]),

  wxSplitterWindow:splitVertically(LeftSplitter, LBPanel, CodePanel,
    [{sashPosition,150}]),

  %% Demo:
  {DemoPanel, [], DemoSz} = create_subwindow(UpperSplitter, "显示", []),

  %% UpperSplitter:
  wxSplitterWindow:splitVertically(UpperSplitter, LeftSplitter, DemoPanel,
    [{sashPosition,400}]),

  %% TopSplitter:
  AddEvent = fun(Parent) ->
    EventText = wxTextCtrl:new(Parent,
      ?wxID_ANY,
      [{style, ?wxTE_DONTWRAP bor
        ?wxTE_MULTILINE bor ?wxTE_READONLY}
      ]),
    wxTextCtrl:appendText(EventText, "Welcome\n"),
    EventText
             end,

  {EvPanel, [EvCtrl],_} = create_subwindow(TopSplitter, "输出", [AddEvent]),

  wxSplitterWindow:splitHorizontally(TopSplitter, UpperSplitter, EvPanel,
    [{sashPosition,0}]),

  wxFrame:show(Frame),

  State = #state{win=Frame, demo={DemoPanel,DemoSz}, selector=LB, log=EvCtrl, doc=Doc},
  %% Load the first example:
  Ex = wxListBox:getStringSelection(LB),
  process_flag(trap_exit, true),
  ExampleObj = load_example(Ex, State),
  wxSizer:add(DemoSz, ExampleObj, [{proportion,1}, {flag, ?wxEXPAND}]),
  wxSizer:layout(DemoSz),

  %% The windows should be set up now, Reset Gravity so we get what we want
  wxSplitterWindow:setSashGravity(TopSplitter,   1.0),
  wxSplitterWindow:setSashGravity(UpperSplitter, 0.0),
  wxSplitterWindow:setSashGravity(LeftSplitter,  0.0),
  wxSplitterWindow:setMinimumPaneSize(TopSplitter, 1),
  wxSplitterWindow:setMinimumPaneSize(UpperSplitter, 1),
  wxSplitterWindow:setMinimumPaneSize(LeftSplitter, 1),

  wxToolTip:enable(true),
  wxToolTip:setDelay(500),

  ok = wxFrame:setStatusText(Frame, "欢迎使用乐基工具包!",[]),
  {Frame, State#state{example=ExampleObj}}.

create_subwindow(Parent, BoxLabel, Funs) ->
  Panel = wxPanel:new(Parent),
  Sz    = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, BoxLabel}]),
  wxPanel:setSizer(Panel, Sz),
  Ctrls = [Fun(Panel) || Fun <- Funs],
  [wxSizer:add(Sz, Ctrl, [{proportion, 1}, {flag, ?wxEXPAND}])
    || Ctrl <- Ctrls],
  {Panel, Ctrls, Sz}.

%%%%%%%%%%%%
%% Callbacks

%% Handled as in normal gen_server callbacks
handle_info({'EXIT',_, wx_deleted}, State) ->
  {noreply,State};
handle_info({'EXIT',_, shutdown}, State) ->
  {noreply,State};
handle_info({'EXIT',_, normal}, State) ->
  {noreply,State};
handle_info(Msg, State) ->
  io:format("Got Info ~p~n",[Msg]),
  {noreply,State}.

handle_call(Msg, _From, State) ->
  io:format("Got Call ~p~n",[Msg]),
  {reply,ok,State}.

handle_cast(Msg, State) ->
  io:format("Got cast ~p~n",[Msg]),
  {noreply,State}.

%% Async Events are handled in handle_event as in handle_info
handle_event(#wx{event=#wxCommand{type=command_listbox_selected, cmdString=Ex}},
    State = #state{demo={_,DemoSz}, example=Example, doc=Doc}) ->
  case Ex of
    [] ->
      {noreply, State};
    _  ->
      wxSizer:detach(DemoSz, Example),
      wx_object:call(Example, shutdown),
      unload_code(Doc),
      NewExample = load_example(Ex, State),
      wxSizer:add(DemoSz, NewExample, [{proportion,1}, {flag, ?wxEXPAND}]),
      wxSizer:layout(DemoSz),
      {noreply, State#state{example=NewExample}}
  end;
handle_event(#wx{id = Id,
  event = #wxCommand{type = command_menu_selected}},
    State = #state{}) ->
  case Id of
    ?wxID_PRINT ->
      %% If you are going to printout mainly text it is easier if
      %% you generate HTML doc and use a wxHtmlEasyPrint
      %% instead of using DCs
      Module = "lj_" ++ wxListBox:getStringSelection(State#state.selector) ++ ".erl",
      HEP = wxHtmlEasyPrinting:new([{name, "Print"},
        {parentWindow, State#state.win}]),
      Html = demo_html_tagger:erl2htmltext(Module),
      wxHtmlEasyPrinting:previewText(HEP, Html),
      {noreply, State};
    ?DEBUG_TRACE ->
      wx:debug(trace),
      {noreply, State};
    ?DEBUG_DRIVER ->
      wx:debug(driver),
      {noreply, State};
    ?DEBUG_VERBOSE ->
      wx:debug(verbose),
      {noreply, State};
    ?DEBUG_NONE ->
      wx:debug(none),
      {noreply, State};
    ?wxID_HELP ->
      new_mini_frame(State#state.win),
      {noreply, State};
    ?wxID_ABOUT ->
      WxWVer = io_lib:format("~p.~p.~p.~p",
        [?wxMAJOR_VERSION, ?wxMINOR_VERSION,
          ?wxRELEASE_NUMBER, ?wxSUBRELEASE_NUMBER]),
      application:load(wx),
      {ok, WxVsn} = application:get_key(wx,  vsn),
      AboutString =
        "Demo of various widgets\n"
        "Authors: Olle & Dan\n\n" ++
        "Frontend: wx-" ++ WxVsn ++
        "\nBackend: wxWidgets-" ++ lists:flatten(WxWVer),

      wxMessageDialog:showModal(wxMessageDialog:new(State#state.win, AboutString,
        [{style,
          ?wxOK bor
            ?wxICON_INFORMATION bor
            ?wxSTAY_ON_TOP},
          {caption, "About"}])),
      {noreply, State};
    ?wxID_EXIT ->
      wx_object:call(State#state.example, shutdown),
      {stop, normal, State};
    _ ->
      {noreply, State}
  end;
handle_event(#wx{event=#wxClose{}}, State = #state{win=Frame}) ->
  io:format("~p Closing window ~n",[self()]),
  ok = wxFrame:setStatusText(Frame, "Closing...",[]),
  {stop, normal, State};
handle_event(Ev,State) ->
  io:format("~p Got event ~p ~n",[?MODULE, Ev]),
  {noreply, State}.

code_change(_, _, State) ->
  {stop, not_yet_implemented, State}.

terminate(_Reason, State = #state{win=Frame}) ->
    catch wx_object:call(State#state.example, shutdown),
  wxFrame:destroy(Frame),
  io:format("~p terminate ~n",[self()]),
  wx:destroy(),
  %%命令行强制退出erl.exe进程
  os:cmd("taskkill /f /t /im erl.exe").

%%%%%%%%%%%%%%%%% Internals %%%%%%%%%%

load_example(Ex, #state{win=Frame, demo={DemoPanel,DemoSz}, log=EvCtrl, doc=Doc}) ->
  ModStr = "lj_" ++ Ex,
  Mod = list_to_atom(ModStr),
  ModFile = ModStr ++ ".txt",

  case file:read_file(ModFile) of
    {ok,_}->
      load_code(Doc, file:read_file(ModFile));
    {error,_}->
      load_code(Doc, file:read_file("defultDoc.txt"))
  end,

  Mod:start([{parent, DemoPanel},{win,Frame}, {demo_sz, DemoSz}, {log, EvCtrl}]).

-define(stc, wxStyledTextCtrl).

code_area(Parent) ->
  FixedFont = wxFont:new(10, ?wxFONTFAMILY_TELETYPE, ?wxNORMAL, ?wxNORMAL,[]),
  Ed = wxStyledTextCtrl:new(Parent),

  ?stc:styleClearAll(Ed),
  ?stc:styleSetFont(Ed, ?wxSTC_STYLE_DEFAULT, FixedFont),
  ?stc:setLexer(Ed, ?wxSTC_LEX_ERLANG),
  ?stc:setMarginType(Ed, 0, ?wxSTC_MARGIN_NUMBER),
  LW = ?stc:textWidth(Ed, ?wxSTC_STYLE_LINENUMBER, "9"),
  ?stc:setMarginWidth(Ed, 0, LW),
  ?stc:setMarginWidth(Ed, 1, 0),

  ?stc:setSelectionMode(Ed, ?wxSTC_SEL_LINES),
  %%?stc:hideSelection(Ed, true),

  Styles =  [{?wxSTC_ERLANG_DEFAULT,  {0,0,0}},
    {?wxSTC_ERLANG_COMMENT,  {160,53,35}},
    {?wxSTC_ERLANG_VARIABLE, {150,100,40}},
    {?wxSTC_ERLANG_NUMBER,   {5,5,100}},
    {?wxSTC_ERLANG_KEYWORD,  {130,40,172}},
    {?wxSTC_ERLANG_STRING,   {170,45,132}},
    {?wxSTC_ERLANG_OPERATOR, {30,0,0}},
    {?wxSTC_ERLANG_ATOM,     {0,0,0}},
    {?wxSTC_ERLANG_FUNCTION_NAME, {64,102,244}},
    {?wxSTC_ERLANG_CHARACTER,{236,155,172}},
    {?wxSTC_ERLANG_MACRO,    {40,144,170}},
    {?wxSTC_ERLANG_RECORD,   {40,100,20}},
    {?wxSTC_ERLANG_SEPARATOR,{0,0,0}},
    {?wxSTC_ERLANG_NODE_NAME,{0,0,0}},
    %% Optional 2.9 stuff
    {?wxSTC_ERLANG_COMMENT_FUNCTION, {160,53,35}},
    {?wxSTC_ERLANG_COMMENT_MODULE, {160,53,35}},
    {?wxSTC_ERLANG_COMMENT_DOC, {160,53,35}},
    {?wxSTC_ERLANG_COMMENT_DOC_MACRO, {160,53,35}},
    {?wxSTC_ERLANG_ATOM_QUOTED, {0,0,0}},
    {?wxSTC_ERLANG_MACRO_QUOTED, {40,144,170}},
    {?wxSTC_ERLANG_RECORD_QUOTED, {40,100,20}},
    {?wxSTC_ERLANG_NODE_NAME_QUOTED, {0,0,0}},
    {?wxSTC_ERLANG_BIFS, {130,40,172}},
    {?wxSTC_ERLANG_MODULES, {64,102,244}},
    {?wxSTC_ERLANG_MODULES_ATT, {64,102,244}}
  ],
  SetStyle = fun({Style, Color}) ->
    ?stc:styleSetFont(Ed, Style, FixedFont),
    ?stc:styleSetForeground(Ed, Style, Color)
             end,
  [SetStyle(Style) || Style <- Styles],
  ?stc:setKeyWords(Ed, 0, keyWords()),

  %% Scrolling
  Policy = ?wxSTC_CARET_SLOP bor ?wxSTC_CARET_JUMPS bor ?wxSTC_CARET_EVEN,
  ?stc:setYCaretPolicy(Ed, Policy, 3),
  ?stc:setVisiblePolicy(Ed, Policy, 3),

  %% ?stc:connect(Ed, stc_doubleclick),
  ?stc:setReadOnly(Ed, true),
  Ed.

load_code(Ed, {ok, Doc}) ->
  ?stc:setReadOnly(Ed, false),
  ?stc:setTextRaw(Ed, <<Doc/binary, 0:8>>),
  Lines = ?stc:getLineCount(Ed),
  Sz = trunc(math:log10(Lines))+1,
  LW = ?stc:textWidth(Ed, ?wxSTC_STYLE_LINENUMBER, lists:duplicate(Sz, $9)),
  %%io:format("~p ~p ~p~n", [Lines, Sz, LW]),
  ?stc:setMarginWidth(Ed, 0, LW+5),
  ?stc:setReadOnly(Ed, true),
  Ed.

unload_code(Ed) ->
  ?stc:setReadOnly(Ed, false),
  ?stc:setTextRaw(Ed, <<0:8>>),
  ?stc:setReadOnly(Ed, true),
  Ed.

keyWords() ->
  L = ["after","begin","case","try","cond","catch","andalso","orelse",
    "end","fun","if","let","of","query","receive","when","bnot","not",
    "div","rem","band","and","bor","bxor","bsl","bsr","or","xor"],
  lists:flatten([K ++ " " || K <- L] ++ [0]).

new_mini_frame(Parent) ->
  MiniFrame = wxMiniFrame:new(Parent, ?wxID_ANY, "乐基工具包", [{style,
    ?wxDEFAULT_FRAME_STYLE bor
      ?wxFRAME_FLOAT_ON_PARENT}]),
  Panel = wxPanel:new(MiniFrame, []),

  Text = "帮助:
  工具包是可扩展的工具集合,
  如果想开发需要的工具,
  可直接编写工具模块,
  然后编译放入工具目录下即可.
  如有问题,请联系工具开发相关人员.
  工具当前版本 0.0.1
  ",

  wxStaticText:new(Panel, ?wxID_ANY, Text, [{style, ?wxALIGN_CENTER}]),
  wxMiniFrame:setSize(MiniFrame, {250,200}),
  wxMiniFrame:center(MiniFrame),
  wxMiniFrame:show(MiniFrame).

